﻿using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

namespace PASPlugin
{
    /// <summary>
    /// Pascal highlighter
    /// </summary>
    public class PASPlugin : Interface.IPlugin
    {
        private string[] keywords = { "and", "array", "asm", "begin", "case", 
                                       "cdecl", "class", "const", "constructor", "default", 
                                       "destructor", "div", "do", "downto", "else", 
                                       "end", "end.", "except", "exit", "exports",
                                       "external", "far", "goto", "finalization", "finally",
                                       "for", "function", "if", "implementation", "in",
                                       "index", "inherited", "initialization", "inline", "interface",
                                       "label", "library", "message", "mod", "near",
                                       "nil", "not", "object", "of", "on",
                                       "or", "out", "overload", "override", "packed",
                                       "pascal", "private", "procedure", "program", "property", "protected",
                                       "public", "published", "raise", "read", "record", 
                                       "register", "repeat", "resourcestring", "safecall", "set", 
                                       "shl", "shr", "stdcall", "stored", "string", 
                                       "then", "threadvar", "to", "try", "type",
                                       "unit", "until", "uses", "var", "virtual", 
                                       "while", "with", "write", "xor"};

        private string[] preprocessors = {

                                         };
        /// <summary>
        /// Plugin name
        /// </summary>
        public string Name
        {
            get
            {
                return "Sourcecode2HTML Plugin for *.pas (Pascal)";
            }
        }
        /// <summary>
        /// Plugin version
        /// </summary>
        public string Version
        {
            get
            {
                return "0.1";
            }
        }
        /// <summary>
        /// Author
        /// </summary>
        public string Author
        {
            get
            {
                return "Евгений Кукло, Евгений Казбан, Власов Иван";
            }
        }
        /// <summary>
        /// Language name
        /// </summary>
        public string LanguageName
        {
            get
            {
                return "Pascal";
            }
        }

        /// <summary>
        /// Extension
        /// </summary>
        public string Extension
        {
            get
            {
                return "pas";
            }
        }

        /// <summary>
        /// Converts source code to HTML string
        /// </summary>
        /// <param name="input">Input string</param>
        /// <param name="linenumbers">Draw line numbers</param>
        /// <returns>Returns HTML string</returns>
        public string HighlightKeywords(string input, bool linenumbers)
        {

            bool CaseSensitive = false;
            string CommentRegEx = @"{.*?\}|//.*?(?=\r|\n)";
            string StringRegEx = @"@?""""|@?"".*?(?!\\).""|''|'.*?(?!\\).'";
            string Keywords = "";
            string Preprocessors = "";
            for (int k = 0; k < keywords.Length; k++)
                Keywords += keywords[k] + " ";
            for (int j = 0; j < preprocessors.Length; j++)
                Preprocessors += preprocessors[j] + " ";
            Preprocessors = Preprocessors.Trim();
            Keywords = Keywords.Trim();
            Regex r;
            Regex codeRegex;
            r = new Regex(@"\w+|-\w+|#\w+|@@\w+|#(?:\\(?:s|w)(?:\*|\+)?\w+)+|@\\w\*+");
            string regKeyword = r.Replace(Keywords, @"(?<=^|\W)$0(?=\W)");
            string regPreproc = r.Replace(Preprocessors, @"(?<=^|\s)$0(?=\s|$)");
            r = new Regex(@" +");
            regKeyword = r.Replace(regKeyword, @"|");
            regPreproc = r.Replace(regPreproc, @"|");

            if (regPreproc.Length == 0)
                regPreproc = "(?!.*)_{37}(?<!.*)";

            StringBuilder regAll = new StringBuilder();
            regAll.Append("(");
            regAll.Append(CommentRegEx);
            regAll.Append(")|(");
            regAll.Append(StringRegEx);
            if (regPreproc.Length > 0)
            {
                regAll.Append(")|(");
                regAll.Append(regPreproc);
            }
            regAll.Append(")|(");
            regAll.Append(regKeyword);
            regAll.Append(")");


            RegexOptions caseInsensitive = CaseSensitive ? 0 : RegexOptions.IgnoreCase;
            codeRegex = new Regex(regAll.ToString(), RegexOptions.Singleline | caseInsensitive);

            StringBuilder sb = new StringBuilder(input);
            sb.Replace("&", "&amp;");
            sb.Replace("<", "&lt;");
            sb.Replace(">", "&gt;");
            sb.Replace("\t", string.Empty.PadRight(4));
            input = codeRegex.Replace(sb.ToString(), new MatchEvaluator(this.MatchEval));
            sb = new StringBuilder();

            sb.Append("<div class=\"csharpcode\">\n");
            StringReader reader = new StringReader(input);
            int i = 0;
            string spaces = "    ";
            int order;
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                i++;
                if ((i % 2) == 1)
                {
                    sb.Append("<pre class=\"alt\">");
                }
                else
                {
                    sb.Append("<pre>");
                }
                if (linenumbers)
                {
                    order = (int)Math.Log10(i);
                    sb.Append("<span class=\"lnum\">"
                        + spaces.Substring(0, 3 - order) + i.ToString()
                        + ":  </span>");
                }
                if (line.Length == 0)
                    sb.Append("&nbsp;");
                else
                    sb.Append(line);
                sb.Append("</pre>\n");
            }
            reader.Close();
            sb.Append("</div>");

            return sb.ToString();
        }

        /// <summary>
        /// Check matches and convert to HTML tags
        /// </summary>
        /// <param name="match">Match</param>
        /// <returns>Converted string</returns>
        protected string MatchEval(Match match)
        {
            if (match.Groups[1].Success)
            {
                StringReader reader = new StringReader(match.ToString());
                string line;
                StringBuilder sb = new StringBuilder();
                while ((line = reader.ReadLine()) != null)
                {
                    if (sb.Length > 0)
                    {
                        sb.Append("\n");
                    }
                    sb.Append("<span class=\"rem\">");
                    sb.Append(line);
                    sb.Append("</span>");
                }
                return sb.ToString();
            }
            if (match.Groups[2].Success)
            {
                return "<span class=\"str\">" + match.ToString() + "</span>";
            }
            if (match.Groups[3].Success)
            {
                return "<span class=\"preproc\">" + match.ToString() + "</span>";
            }
            if (match.Groups[4].Success)
            {
                return "<span class=\"kwrd\">" + match.ToString() + "</span>";
            }
            System.Diagnostics.Debug.Assert(false, "None of the above!");
            return "";
        }
    }
}
